package state


import "number"


// 对于长度运算符(#)
// Lua首先判断值是否是字符串
// 如果是，结果就是字符串长度
// 否则看值是否有__len元方法
// 如果有，则以值为参数调用元方法，将元方法返回值作为结果
// 如果找不到对应元方法，但值是表，结果就是表的长度
func (self *luaState) Len(idx int) {

	val := self.stack.get(idx)

	if s, ok := val.(string); ok {

		self.stack.push(int64(len(s)))

	} else if result, ok := callMetamethod(val, val, "__len", self); ok {

		self.stack.push(result)

	} else if t, ok := val.(*luaTable); ok {

		self.stack.push(int64(t.len()))

	} else {

		panic("length error!")

	}

}


// 从栈顶弹出n个值，对这些值进行拼接
// 然后把结果推入栈顶
// 如果n是0，不弹出任何值，直接往栈顶推入一个空字符串即可
// 否则，将栈顶的两个值都弹出，进行拼接
// 这个过程一直进行，直到n个值都处理完毕为止
// 如果操作数不是字符串(或数字)
// 尝试调用__concat元方法，查找和调用规则同二元算术元方法
func (self *luaState) Concat(n int) {

	if n == 0 {
		self.stack.push("")
	} else if n >= 2 {
		for i := 1; i < n; i++ {

			if self.IsString(-1) && self.IsString(-2) {
				s2 := self.ToString(-1)
				s1 := self.ToString(-2)
				self.stack.pop()
				self.stack.pop()
				self.stack.push(s1 + s2)
				continue
			}

			b := self.stack.pop()
			a := self.stack.pop()

			if result, ok := callMetamethod(a, b, "__concat", self); ok {

				self.stack.push(result)

				continue
			}

			panic("concatenation error!")
		}
	}

	// n == 1, do nothing

}


// 根据键获取表的下一个键值对
// 其中表的索引由参数指定，上一个键从栈顶弹出
// 如果从栈顶弹出的键是nil，说明刚开始遍历表，把表的第一个键值对推入栈顶并返回true
// 否则，如果遍历还没有结束，把下一个键值对推入栈顶并返回true
// 如果表是空的，或者遍历已经结束，不用往栈里推入任何值，直接返回false即可
func (self *luaState) Next(idx int) bool {

	val := self.stack.get(idx)

	if t, ok := val.(*luaTable); ok {

		key := self.stack.pop()

		if nextKey := t.nextKey(key); nextKey != nil {

			self.stack.push(nextKey)
			self.stack.push(t.get(nextKey))

			return true
		}

		return false
	}

	panic("table expected!")
}


// 从栈顶弹出一个Lua值，把该值作为错误抛出
// 虽然方法以整数作为返回值，但是实际上该方法是没有办法正常返回的
// 之所以有返回值，完全是为了方便用户使用
func (self *luaState) Error() int {

	err := self.stack.pop()

	panic(err)

}


func (self *luaState) StringToNumber(s string) bool {
	if n, ok := number.ParseInteger(s); ok {
		self.PushInteger(n)
		return true
	}
	if n, ok := number.ParseFloat(s); ok {
		self.PushNumber(n)
		return true
	}
	return false
}